package com.boe.config.swagger;


import com.boe.common.util.ListUtil;
import com.boe.common.util.StringUtil;
import com.boe.config.spring.SpringUtil;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping;
import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.ContentCachingResponseWrapper;
import springfox.documentation.annotations.ApiIgnore;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.oas.annotations.EnableOpenApi;
import springfox.documentation.service.ApiKey;
import springfox.documentation.service.AuthorizationScope;
import springfox.documentation.service.SecurityReference;
import springfox.documentation.service.SecurityScheme;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.spring.web.plugins.WebFluxRequestHandlerProvider;
import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;


/**
 * springfox-swagger-ui 3.0.0 访问地址： /swagger-ui/index.html
 */
/*
HandlerInterceptor 对Swaager请求放行

var beanType = handler.beanType;

if (beanType.name.IsIn(
        listOf(
            // Swagger2.9.2
            "springfox.documentation.swagger.web.ApiResourceController",
            //Swagger3.0.0
            "springfox.documentation.swagger2.web.Swagger2ControllerWebMvc",
            "springfox.documentation.oas.web.OpenApiControllerWebMvc"
        )
    )
) {
    return true;
}
 */
@Configuration
@EnableOpenApi
class Swagger3Config {
    public Docket getDocket(
            String groupName,
            List<RequestMethod> methods,
            Function<String, Boolean> pathFunc
    ) {
        var enable = Arrays.stream(SpringUtil.getEnvironment().getActiveProfiles())
                .anyMatch(it -> !it.equals("test") && !it.equals("main"));

        var applicationName = SpringUtil.getEnvironment().getProperty("spring.application.name");
        var apiBuilder = new ApiInfoBuilder();

        if (StringUtil.hasValue(groupName)) {
            apiBuilder.title(applicationName + " - " + groupName);
        } else {
            apiBuilder.title(applicationName);
        }
        apiBuilder.description("地址栏添加 ?docExpansion=list 展开API");


        var docket = new Docket(DocumentationType.OAS_30)
                .useDefaultResponseMessages(true)
                .ignoredParameterTypes(
                        HttpServletRequest.class,
                        HttpServletResponse.class,
                        ContentCachingRequestWrapper.class,
                        ContentCachingResponseWrapper.class
                )
                .apiInfo(apiBuilder.build())
                .enable(enable)
                .ignoredParameterTypes(ApiIgnore.class);

        if (StringUtil.hasValue(groupName)) {
            docket.groupName(groupName);
        }

        var selectBuilder = docket.select();

        if (ListUtil.any(methods)) {
            selectBuilder.apis(it ->
                    ListUtil.any(ListUtil.intersect(it.supportedMethods(), methods))
            );

        } else {
            selectBuilder.apis(it ->
                    ListUtil.any(ListUtil.intersect(it.supportedMethods(), Arrays.asList(RequestMethod.GET, RequestMethod.POST)))
            );
        }

        if (pathFunc != null) {
            selectBuilder
                    .paths(it ->
                            pathFunc.apply(it)
                    );

        }
        return selectBuilder.build()
                .securitySchemes(securitySchemes())
                .securityContexts(securityContexts());

    }

    private List<SecurityContext> securityContexts() {

        var authorizationScope = new AuthorizationScope("global", "accessEverything");
        var securityReferences = Arrays.asList(new SecurityReference("token", new AuthorizationScope[]{authorizationScope}));


        return Arrays.asList(
                SecurityContext.builder()
                        .securityReferences(securityReferences)
                        .build()
        );
    }

    private List<SecurityScheme> securitySchemes() {
        return Arrays.asList(new ApiKey("token", "token", "header"));
    }

    @Bean
    public Docket allSwagger() {
        return getDocket("全部", Arrays.asList(), null);
    }

    @Bean
    public Docket postSwagger() {
        return getDocket("POST请求", Arrays.asList(RequestMethod.POST), null);
    }


    @Bean
    public Docket getSwagger() {
        return getDocket("GET请求", Arrays.asList(RequestMethod.GET), null);
    }

    @Bean
    public Docket listSwagger() {
        return getDocket("查询列表", Arrays.asList(), it -> it.contains("/list"));
    }


    @Bean
    public BeanPostProcessor springfoxHandlerProviderBeanPostProcessor() {
        return new BeanPostProcessor() {
            @Override
            public Object postProcessAfterInitialization(Object bean, String beanName) {
                if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) {
                    customizeSpringfoxHandlerMappings(getHandlerMappings(bean));
                }
                return bean;
            }


            private <T extends RequestMappingInfoHandlerMapping> void customizeSpringfoxHandlerMappings(List<T> mappings) {
                var copy = mappings.stream()
                        .filter(mapping -> mapping.getPatternParser() == null
                        )
                        .collect(Collectors.toList());
                mappings.clear();
                mappings.addAll(copy);
            }

            private List<RequestMappingInfoHandlerMapping> getHandlerMappings(Object bean) {
                try {
                    var field = ReflectionUtils.findField(bean.getClass(), "handlerMappings");
                    field.setAccessible(true);
                    return (List<RequestMappingInfoHandlerMapping>) field.get(bean);
                } catch (Exception e) {
                    throw new RuntimeException(e.getMessage(), e);
                }
            }
        };
    }
}